%reload_ext autoreload
%autoreload 2
%matplotlib inline
import pandas as pd
import numpy as np
from datetime import datetime
import matplotlib.pyplot as plt
from sklearn import cluster, metrics, decomposition, preprocessing
import plotly.io as pio
pio.renderers.default='notebook'
import plotly.express as px
import plotly.graph_objects as go
# Import des modules contenant les fonctions utilitaires
import src.data_helpers as dth
# Réglage des graphiques
plt.style.use('seaborn-whitegrid')
plt.rc('font', size=12)
plt.rc('axes', titlesize=20)
plt.rc('axes', labelsize=20)
plt.rc('xtick', labelsize=12)
plt.rc('ytick', labelsize=12)
plt.rc('legend', fontsize=12)
dims_fig = (25,20)
orders_merge = pd.read_csv('merge.csv', sep=',')
On va utiliser le modèle identifié dans le notebook "model", comme étant le plus simple pour segmenter les clients les plus importants, c'est à dire ceux qui ont passé plus d'une commande ou dépensent de gros montants.
Il s'agit du modèle kMeans avec 5 clusters et les features RFM (Récence, Fréquence, Montant cumulé)
rfm = orders_merge.groupby('customer_unique_id').agg(
recence = ("order_purchase_timestamp", "max"),
frequence = ("customer_id", "count"),
montant_cumulé = ("payment_value", "sum")
)
date_format = '%Y-%m-%d %H:%M:%S'
max_date = datetime.strptime(rfm['recence'].max(), date_format)
rfm['recence'] = rfm['recence'].transform(lambda x: (max_date - datetime.strptime(x, date_format)).days)
rfm
| recence | frequence | montant_cumulé | |
|---|---|---|---|
| customer_unique_id | |||
| 0000366f3b9a7992bf8c76cfdf3221e2 | 160 | 1 | 141.90 |
| 0000b849f77a49e4a4ce2b2a4ca5be3f | 163 | 1 | 27.19 |
| 0000f46a3911fa3c0805444483337064 | 585 | 1 | 86.22 |
| 0000f6ccb0745a6a4b88665a16c9f078 | 369 | 1 | 43.62 |
| 0004aac84e0df4da2b147fca70cf8255 | 336 | 1 | 196.89 |
| ... | ... | ... | ... |
| fffcf5a5ff07b0908bd4e2dbc735a684 | 495 | 1 | 2067.42 |
| fffea47cd6d3cc0a88bd621562a9d061 | 310 | 1 | 84.58 |
| ffff371b4d645b6ecea244b27531430a | 617 | 1 | 112.46 |
| ffff5962728ec6157033ef9805bacc48 | 168 | 1 | 133.69 |
| ffffd2657e2aad2907e67c3e9daecbeb | 532 | 1 | 71.56 |
96096 rows × 3 columns
On va évaluer la performance de notre modèle au cours du temps sur les données que l'on a en notre possession, en jouant sur la fréquence de mise à jour de ces données.
L'objectif est de trouver à partir de qu'elle fréquence le modèle se dégrade, c'est à dire un seuil à partir duquel les prédictions entre le modèle d'origine et un nouveau modèle entraîné sont trop différentes.
def calculateAriScore(data, time):
result = []
for i in range (500, time, -time):
# t0
f0 = data[data['recence'] > i]
s0 = preprocessing.StandardScaler().fit(f0)
f0_std = pd.DataFrame(
s0.transform(f0),
columns=f0.columns
)
m0 = cluster.KMeans(5)
m0.fit(f0_std)
# t1 = t0 + n days
f1 = data[data['recence'] > i-time]
s1 = preprocessing.StandardScaler().fit(f1)
f1_std = pd.DataFrame(
s1.transform(f1),
columns=f1.columns
)
m1 = cluster.KMeans(5)
c1 = m1.fit_predict(f1_std)
# Comparaison entre t0 et t1
f1_0_std = pd.DataFrame(
s0.transform(f1),
columns=f1.columns
)
c1_0 = m0.predict(f1_0_std)
score = metrics.adjusted_rand_score(c1_0, c1)
result.append(score)
return result
frequences = [3, 7, 15, 30, 60, 120]
for i in frequences:
result = calculateAriScore(rfm, i)
fig = px.line(
result,
height=300,
width=800,
title=f"Score ARI par interval de {i} jours (moyenne = {sum(result)/len(result)})"
)
fig.show()
def calculateAriScoreFromOrigin(data, time):
result = []
# t0
f0 = data[data['recence'] > 500]
s0 = preprocessing.StandardScaler().fit(f0)
f0_std = pd.DataFrame(
s0.transform(f0),
columns=f0.columns
)
m0 = cluster.KMeans(5)
m0.fit(f0_std)
for i in range (500, time, -time):
# tn = t0 + n days
f1 = data[data['recence'] > i-time]
s1 = preprocessing.StandardScaler().fit(f1)
f1_std = pd.DataFrame(
s1.transform(f1),
columns=f1.columns
)
m1 = cluster.KMeans(5)
c1 = m1.fit_predict(f1_std)
# Comparaison entre t0 et t1
f1_0_std = pd.DataFrame(
s0.transform(f1),
columns=f1.columns
)
c1_0 = m0.predict(f1_0_std)
score = metrics.adjusted_rand_score(c1_0, c1)
result.append(score)
return result
result = calculateAriScoreFromOrigin(rfm, 1)
fig = px.line(
result,
title=f"Evolution du score ARI dans le temps",
width=800,
)
fig.show()
Si l'on fixe le seuil du score ARI à 0.8, il faudrait donc réentraîner le modèle tous les mois en y ajoutant les nouvelles données.
Si le modèle n'est pas réentraîné dans le temps, le clustering collera de moins en moins aux nouvelles données, et l'on risque de faire de mauvaises prédiction et donc un mauvais ciblage de clientèle.